]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
main: Correct the pointer value of strtab on riscv/mips architectures
authorDaniel van Vugt <daniel.van.vugt@canonical.com>
Thu, 23 Mar 2023 09:51:46 +0000 (17:51 +0800)
committerMarge Bot <marge-bot@gnome.org>
Sat, 5 Aug 2023 19:19:29 +0000 (19:19 +0000)
Glibc defines `d_ptr`/`d_val` to be a relocated address on most
architectures, except for riscv and mips where it is just an offset from
the binary load address. So we need to convert `strtab` from an offset
into a pointer for riscv and mips.

Internally within glibc there is the `D_PTR` macro for doing this, which
relies on private data hidden in an ABI-unstable part of `link_map`. So
we can't use the same conditional logic as glibc does internally.

Our solution is to detect when `strtab` is unreasonably low (below the
address of our binary) and so it must be an unrelocated offset. In that
case we just do the relocation manually.

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6528
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2718>

src/main.c

index bbf84599913f4fa9624a89cffd55a6e669c412f2..a19c452a2a1e66b6e0b754a58eb5f7b5e04f9053 100644 (file)
@@ -1,5 +1,7 @@
 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 
+#define _GNU_SOURCE
+
 #include "config.h"
 
 #if defined (HAVE_MALLINFO) || defined (HAVE_MALLINFO2)
@@ -20,6 +22,7 @@
 #include <link.h>
 
 #ifdef HAVE_EXE_INTROSPECTION
+#include <dlfcn.h>
 #include <elf.h>
 #endif
 
@@ -140,6 +143,8 @@ maybe_add_rpath_introspection_paths (void)
   g_auto (GStrv) paths = NULL;
   g_autofree char *exe_dir = NULL;
   GStrv str;
+  Dl_info dl_info;
+  struct link_map *link_map = NULL;
 
   for (dyn = _DYNAMIC; dyn->d_tag != DT_NULL; dyn++)
     {
@@ -154,6 +159,21 @@ maybe_add_rpath_introspection_paths (void)
   if ((!rpath && !runpath) || !strtab)
     return;
 
+  if (dladdr1 (_DYNAMIC, &dl_info, (void **) &link_map, RTLD_DL_LINKMAP))
+    {
+      /* Sanity check */
+      g_return_if_fail ((void *) _DYNAMIC == (void *) link_map->l_ld);
+
+      /* strtab should be at an offset above our load address. If it's not
+       * then this is a special architecture (riscv, mips) that has a
+       * readonly _DYNAMIC section that's not relocated. So in that case
+       * strtab is currently an offset rather than an address. Let's make it
+       * an address...
+       */
+      if (strtab < (const char *) link_map->l_addr)
+        strtab += link_map->l_addr;
+    }
+
   if (rpath)
     paths = g_strsplit (strtab + rpath->d_un.d_val, ":", -1);
   else